#include <stdio.h>
#include "lib/tokenizer.h"
#include "lib/util.h"

struct test_case {
  char  *str;
  char **argv;
  int    argc;
};
static int NUM_TESTS = 0;
static struct test_case **TESTS = NULL;

void init (void)
{
  NUM_TESTS = 0;
  TESTS = (struct test_case **) run2_malloc (6 * sizeof (struct test_case *));

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("How, now, \"brown cow\"");
  TESTS[NUM_TESTS]->argc = 3;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = run2_strdup ("How,");
  TESTS[NUM_TESTS]->argv[1] = run2_strdup ("now,");
  TESTS[NUM_TESTS]->argv[2] = run2_strdup ("brown cow");
  TESTS[NUM_TESTS]->argv[3] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("ab\\' cd\\\"ef");
  TESTS[NUM_TESTS]->argc = 2;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = run2_strdup ("ab\'");
  TESTS[NUM_TESTS]->argv[1] = run2_strdup ("cd\"ef");
  TESTS[NUM_TESTS]->argv[2] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("a b 'd \" e' \"f g\"");
  TESTS[NUM_TESTS]->argc = 4;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = run2_strdup ("a");
  TESTS[NUM_TESTS]->argv[1] = run2_strdup ("b");
  TESTS[NUM_TESTS]->argv[2] = run2_strdup ("d \" e");
  TESTS[NUM_TESTS]->argv[3] = run2_strdup ("f g");
  TESTS[NUM_TESTS]->argv[4] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("C:\\\\\"Program Files\"\\\\\"More Spaces\"\\\\\"Yet More\"");
  TESTS[NUM_TESTS]->argc = 1;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = run2_strdup ("C:\\Program Files\\More Spaces\\Yet More");
  TESTS[NUM_TESTS]->argv[1] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("");
  TESTS[NUM_TESTS]->argc = 0;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = NULL;
  TESTS[NUM_TESTS]->argc = 0;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = NULL;
  NUM_TESTS++;

  TESTS[NUM_TESTS] = (struct test_case *) run2_malloc (sizeof (struct test_case));
  TESTS[NUM_TESTS]->str = run2_strdup ("ab \"\" cd");
  TESTS[NUM_TESTS]->argc = 3;
  TESTS[NUM_TESTS]->argv = run2_malloc ((TESTS[NUM_TESTS]->argc + 1) * sizeof (char *));
  TESTS[NUM_TESTS]->argv[0] = run2_strdup("ab");
  TESTS[NUM_TESTS]->argv[1] = run2_strdup("");
  TESTS[NUM_TESTS]->argv[2] = run2_strdup("cd");
  TESTS[NUM_TESTS]->argv[3] = NULL;
  NUM_TESTS++;
}

void fini(void)
{
  while (NUM_TESTS > 0)
    {
      NUM_TESTS--;
      free (TESTS[NUM_TESTS]->str);
      run2_freeargv (TESTS[NUM_TESTS]->argv);
      free (TESTS[NUM_TESTS]);
      TESTS[NUM_TESTS] = NULL;
    }
  free (TESTS);
}

int do_tests (void)
{
  int i, j;
  int rc = 0;
  int argc = 0;
  const char **argv = NULL;
  for (i = 0; i < NUM_TESTS; i++)
    {
      struct test_case * test = TESTS[i];
      infoMsg ("test %d: str=|%s|", i, (test->str ? test->str : "(null)"));
      if ((rc = run2_split_string (test->str, &argc, &argv)) != 0)
        {
          errorMsg ("FAILED test %d: rc=%d |%s|", i, rc, test->str);
          rc++;
        }
      else
        {
          if (argc != test->argc)
            {
              errorMsg ("FAILED test %d: bad argc (%d != expected %d)", i, argc, test->argc);
              rc++;
              goto cleanup;
            }
          infoMsg ("test %d: argc=%d", i, argc);
          for (j = 0; j < argc; j++)
            {
              if (strcmp (argv[j], test->argv[j]) != 0)
                {
                  errorMsg ("FAILED test %d: bad argv[%d] (|%s| != expected |%s|)",
                            i, j, argv[j], test->argv[j]);
                  rc++;
                  goto cleanup;
                }
              infoMsg ("test %d: argv[%d]=|%s|", i, j, argv[j]);
            }
        }
cleanup:
        if (argv && *argv)
          free (argv); /* the returned argv is a vector of pointers into str! */
        argv = NULL;
        argc = 0;
    }
  return rc;
}

int main (int argc, char * argv[])
{
  int rc = 0;
  run2_set_program_name (argv[0]);
  run2_set_verbose_level (RUN2_DEFAULT_LOG_VERBOSE_LEVEL);
  init();
  rc = do_tests ();
  fini();
  return rc;
}
